using System;
using System.Collections;
using System.Collections.Generic;

namespace Dorm.QueryObject
{
    public class FilterExpression
    {
        public string PropertyName { get; set; }
        public FilterOptions Comparison { get; set; }
        public object CompareValue { get; set; }
        public string Concatenate { get; set; }

        public override string ToString()
        {
            string expression = string.Empty;

            expression += PropertyName;

            switch(Comparison)
            {
                case FilterOptions.Equals: expression += " = "; break;
                case FilterOptions.NotEquals: expression += " <> "; break;
                case FilterOptions.StartsWith: expression += " LIKE "; break;
                case FilterOptions.EndsWith: expression += " LIKE '%' + "; break;
                case FilterOptions.Contains: expression += " LIKE '%' + "; break;
                case FilterOptions.IsNull: expression += " IS NULL "; break;
                case FilterOptions.IsNotNull: expression += " IS NOT NULL "; break;
                case FilterOptions.GreatherThan: expression += " > "; break;
                case FilterOptions.GreatherThanOrEqual: expression += " >= "; break;
                case FilterOptions.LessThan: expression += " < "; break;
                case FilterOptions.LessThanOrEqual: expression += " <= "; break;
                case FilterOptions.In: expression += " IN \r\n("; break;
                case FilterOptions.NotIn: expression += " NOT IN \r\n("; break;
                default: throw new ArgumentOutOfRangeException();
            }

            
            if (Comparison != FilterOptions.IsNull && Comparison != FilterOptions.IsNotNull)
            {
                 #region Teste
                //if (Comparison == FilterOptions.Equals ||
                //    Comparison == FilterOptions.NotEquals ||
                //    Comparison == FilterOptions.GreatherThan ||
                //    Comparison == FilterOptions.GreatherThanOrEqual ||
                //    Comparison == FilterOptions.LessThan ||
                //    Comparison == FilterOptions.LessThanOrEqual)
                //{
                //    if (Parameter.IsNumeric(CompareValue))
                //        expression += CompareValue.ToString();
                //    else
                //        expression += "'" + CompareValue + "'";
                //}
                //else if (Comparison == FilterOptions.StartsWith ||
                //         Comparison == FilterOptions.EndsWith ||
                //         Comparison == FilterOptions.Contains)
                //{
                //    expression += "'" + CompareValue + "'";
                //}
                //else if (Comparison == FilterOptions.In || 
                //         Comparison == FilterOptions.NotIn)
                //{
                //    expression += CompareValue + ")";
                //}
                 #endregion

                if (Comparison == FilterOptions.In || Comparison == FilterOptions.NotIn)
                    expression += CompareValue + ")";
                else
                    expression += Parameter.GetDbValue(CompareValue);
            }

            if (Comparison == FilterOptions.Contains || Comparison == FilterOptions.StartsWith)
            {
                expression += " + '%'";
            }

            return expression;
        }
    }

    public enum FilterOptions
    {
        Equals,
        NotEquals, 
        StartsWith, 
        EndsWith, 
        Contains, 
        IsNull, 
        IsNotNull, 
        GreatherThan, 
        GreatherThanOrEqual, 
        LessThan, 
        LessThanOrEqual,
        In,
        NotIn
    }

    public class FilterBuilder<T>
    {
        private Query<T> parentQuery;
        private string propertyName;
        private FilterOptions filterOption;
        private object compareValue;

        public FilterBuilder(Query<T> _parentQuery, string _propertyName)
        {
            parentQuery = _parentQuery;
            propertyName = _propertyName;
        }

        public new Query<T> Equals(object CompareValue)
        {
            filterOption = FilterOptions.Equals;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> NotEquals(object CompareValue)
        {
            filterOption = FilterOptions.NotEquals;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> StartsWith(object CompareValue)
        {
            filterOption = FilterOptions.StartsWith;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> EndsWith(object CompareValue)
        {
            filterOption = FilterOptions.EndsWith;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> Contains(object CompareValue)
        {
            filterOption = FilterOptions.Contains;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> IsNull()
        {
            filterOption = FilterOptions.IsNull;            
            return ReturnQuery();
        }

        public Query<T> IsNotNull()
        {
            filterOption = FilterOptions.IsNotNull;         
            return ReturnQuery();
        }

        public Query<T> GreatherThan(object CompareValue)
        {
            filterOption = FilterOptions.GreatherThan;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> GreatherThanOrEqual(object CompareValue)
        {
            filterOption = FilterOptions.GreatherThanOrEqual;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> LessThan(object CompareValue)
        {
            filterOption = FilterOptions.LessThan;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> LessThanOrEqual(object CompareValue)
        {
            filterOption = FilterOptions.LessThanOrEqual;
            compareValue = CompareValue;
            return ReturnQuery();
        }

        public Query<T> In(string subQuery)
        {
            filterOption = FilterOptions.In;
            compareValue = subQuery;
            return ReturnQuery();
        }

        public Query<T> In(IEnumerable compareValues)
        {
            foreach(var value in compareValues)
            {
                if (value.GetType().GetProperty("ID") != null)
                    compareValue += Parameter.GetDbValue(value.GetType().GetProperty("ID").GetValue(value, null)) + ", ";
                else
                    compareValue += Parameter.GetDbValue(value) + ", ";
            }
            compareValue = compareValue.ToString().Remove(compareValue.ToString().Length - 2, 2);

            filterOption = FilterOptions.In;
            return ReturnQuery();
        }

        public Query<T> NotIn(string subQuery)
        {
            filterOption = FilterOptions.NotIn;
            compareValue = subQuery;
            return ReturnQuery();
        }

        public Query<T> NotIn(IEnumerable compareValues)
        {
            foreach (var value in compareValues)
            {
                if (value.GetType().GetProperty("ID") != null)
                    compareValue += Parameter.GetDbValue(value.GetType().GetProperty("ID").GetValue(value, null)) + ", ";
                else
                    compareValue += Parameter.GetDbValue(value) + ", ";
            }
            compareValue = compareValue.ToString().Remove(compareValue.ToString().Length - 2, 2);

            filterOption = FilterOptions.NotIn;
            return ReturnQuery();
        }

        private Query<T> ReturnQuery()
        {
            if (parentQuery.Filters == null) 
                parentQuery.Filters = new List<FilterExpression>();

            var filterExpression = new FilterExpression { PropertyName = propertyName, CompareValue = compareValue, Comparison = filterOption };
            
            parentQuery.Filters.Add(filterExpression);

            return parentQuery;
        }
    }
}
